/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package diff.viewer;

import java.awt.ComponentOrientation;
import java.awt.EventQueue;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JEditorPane;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;

/**
 *
 * @author WL
 */
public class DiffPane extends javax.swing.JPanel implements DocumentListener, PropertyChangeListener
{
    private DiffSplitPaneUI spui;
    private int horizontalScroll1ChangedValue = -1;
    private int horizontalScroll2ChangedValue = -1;
    private DiffPaneManager manager;
    private LineNumbersBar rightLinesBar;
    private LineNumbersBar leftLinesBar;
    private boolean diffInitialized;
    
    /**
     * Creates new form DiffPane
     */
    public DiffPane() {
        initComponents();
        customInit();
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                balanceSplitting();
            }
        });
    }
    
    public void refresh()
    {
        if (!this.isVisible())
            return;
        
        manager.updateDifference();
        this.revalidate();
        this.repaint();
    }
    
    @Override
    public void setVisible(boolean val)
    {
        if (val && !diffInitialized)
        {
            diffInitialized = true;
            SwingUtilities.invokeLater(new Runnable(){
               public void run()
               {
                   refresh();
               }
            });
        }
        super.setVisible(val);
    }
    
    public void balanceSplitting()
    {
        int dividerSize = jSplitPane1.getDividerSize();
        int width = this.getSize().width;
        jSplitPane1.setDividerLocation(width / 2 - dividerSize / 2);
    }
    
    public void setContentType(String type)
    {
        diffEditor1.setContentType(type);
        diffEditor2.setContentType(type);
    }
    
    private void customInit()
    {
        diffEditor1.setLeft(true);
        joinScrollBars();
        setVerticalScrollBarsListeners();
        
        jScrollPane1.setRowHeaderView(null);
        jScrollPane2.setRowHeaderView(null);
        
        spui = new DiffSplitPaneUI(jSplitPane1);
        jSplitPane1.setUI(spui);
        
        manager = new DiffPaneManager();
        manager.setDiffPane(this);
        spui.divider.setManager(manager);
        
        rightLinesBar = new LineNumbersBar(diffEditor2);
        rightLinesBar.setManager(manager);
        jScrollPane2.setRowHeaderView(rightLinesBar);
        
        leftLinesBar = new LineNumbersBar(diffEditor1);
        leftLinesBar.setManager(manager);
        
        jScrollPane1.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        jScrollPane1.setRowHeaderView(leftLinesBar);
        diffEditor1.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
        jScrollPane1.getViewport().addChangeListener(new ChangeListener(){
            @Override
            public void stateChanged(ChangeEvent e) {
                leftLinesBar.refresh();
                jSplitPane1.repaint();
            }
        });
        
        setListener(diffEditor1.getDocument());
        setListener(diffEditor2.getDocument());
        
        //ensures that diff will still be performed upon a change in content type
        diffEditor1.addPropertyChangeListener(this);
        diffEditor2.addPropertyChangeListener(this);
        jSplitPane1.addPropertyChangeListener(new PropertyChangeListener(){
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if ("dividerLocation".equals(evt.getPropertyName()))
                    delayedRefresh();
            }
        });
    }
    
    @Override
    public void propertyChange(PropertyChangeEvent evt) 
    {
        if ("document".equals(evt.getPropertyName()))
        {
            setListener(diffEditor1.getDocument());
            setListener(diffEditor2.getDocument());
            jScrollPane1.setRowHeaderView(leftLinesBar);
            jScrollPane2.setRowHeaderView(rightLinesBar);
        }
    }
    
    private void setListener(Document doc)
    {
        doc.addDocumentListener(this);
    }
    
    @Override
    public void insertUpdate(DocumentEvent e) {
        delayedRefresh();
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
        delayedRefresh();
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
        delayedRefresh();
    }
    
    public void delayedRefresh()
    {
        SwingUtilities.invokeLater(new Runnable(){
            public void run()
            {
                refresh();
            }
        });
    }    
    
    public JScrollPane getScrollPane1()
    {
        return jScrollPane1;
    }
    
    public JScrollPane getScrollPane2()
    {
        return jScrollPane2;
    }
    
    public JEditorPane getEditorPane1()
    {
        return diffEditor1;
    }
    
    public JEditorPane getEditorPane2()
    {
        return diffEditor2;
    }
    
    private void setVerticalScrollBarsListeners()
    {
        ChangeListener listener = new ChangeListener(){
            @Override
            public void stateChanged(ChangeEvent e) {
                jSplitPane1.repaint();
            }
        };
        JScrollBar scrollBar2 = jScrollPane2.getVerticalScrollBar();
        scrollBar2.getModel().addChangeListener(listener);
    }
    
    private void joinScrollBars() 
    {
        final JScrollBar scrollBarH1 = jScrollPane1.getHorizontalScrollBar();
        final JScrollBar scrollBarH2 = jScrollPane2.getHorizontalScrollBar();

        scrollBarH1.getModel().addChangeListener(new javax.swing.event.ChangeListener()  {
            public void stateChanged(javax.swing.event.ChangeEvent e) {
                int value = scrollBarH1.getValue();
                if (value == horizontalScroll1ChangedValue) return;
                int max1 = scrollBarH1.getMaximum();
                int max2 = scrollBarH2.getMaximum();
                int ext1 = scrollBarH1.getModel().getExtent();
                int ext2 = scrollBarH2.getModel().getExtent();
                value = max1 - ext1 - value; //modification for Right-To-Left component orientation
                if (max1 == ext1) horizontalScroll2ChangedValue = 0;
                else horizontalScroll2ChangedValue = (value*(max2 - ext2))/(max1 - ext1);
                horizontalScroll1ChangedValue = -1;
                scrollBarH2.setValue(horizontalScroll2ChangedValue);
            }
        });
        scrollBarH2.getModel().addChangeListener(new javax.swing.event.ChangeListener()  {
            public void stateChanged(javax.swing.event.ChangeEvent e) {
                int value = scrollBarH2.getValue();
                if (value == horizontalScroll2ChangedValue) return;
                int max1 = scrollBarH1.getMaximum();
                int max2 = scrollBarH2.getMaximum();
                int ext1 = scrollBarH1.getModel().getExtent();
                int ext2 = scrollBarH2.getModel().getExtent();
                if (max2 == ext2) horizontalScroll1ChangedValue = 0;
                else horizontalScroll1ChangedValue = (value*(max1 - ext1))/(max2 - ext2);
                horizontalScroll2ChangedValue = -1;
                horizontalScroll1ChangedValue = max1 - ext1 - horizontalScroll1ChangedValue; //modification for Right-To-Left component orientation           
                scrollBarH1.setValue(horizontalScroll1ChangedValue);
            }
        });
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jSplitPane1 = new javax.swing.JSplitPane();
        jPanel2 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        diffEditor1 = new diff.viewer.DiffEditor();
        jPanel3 = new javax.swing.JPanel();
        jScrollPane2 = new javax.swing.JScrollPane();
        diffEditor2 = new diff.viewer.DiffEditor();

        jSplitPane1.setDividerLocation(600);
        jSplitPane1.setDividerSize(50);

        jScrollPane1.setBorder(null);

        diffEditor1.setBorder(null);
        jScrollPane1.setViewportView(diffEditor1);

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1)
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 762, Short.MAX_VALUE)
        );

        jSplitPane1.setLeftComponent(jPanel2);

        jScrollPane2.setBorder(null);

        diffEditor2.setBorder(null);
        jScrollPane2.setViewportView(diffEditor2);

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 578, Short.MAX_VALUE)
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 762, Short.MAX_VALUE)
        );

        jSplitPane1.setRightComponent(jPanel3);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 1228, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 1228, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 762, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jSplitPane1))
        );
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private diff.viewer.DiffEditor diffEditor1;
    private diff.viewer.DiffEditor diffEditor2;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JSplitPane jSplitPane1;
    // End of variables declaration//GEN-END:variables
}
